package com.yuehuanghun.admin.core.controller.system;

import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.yuehuanghun.admin.core.config.Constants;
import com.yuehuanghun.admin.core.config.SystemProperties;
import com.yuehuanghun.admin.core.config.constant.CacheKeys;
import com.yuehuanghun.admin.core.config.util.SecurityUtils;
import com.yuehuanghun.admin.core.config.util.UserUtils;
import com.yuehuanghun.admin.core.domain.SysLogininfor;
import com.yuehuanghun.admin.core.domain.SysMenu;
import com.yuehuanghun.admin.core.domain.SysUser;
import com.yuehuanghun.admin.core.domain.vo.RouterVo;
import com.yuehuanghun.admin.core.dto.LoginReq;
import com.yuehuanghun.admin.core.service.ISysLogininforService;
import com.yuehuanghun.admin.core.service.ISysMenuService;
import com.yuehuanghun.admin.core.service.ISysUserService;
import com.yuehuanghun.admin.core.service.SysPermissionService;
import com.yuehuanghun.framework.annotation.Log;
import com.yuehuanghun.framework.annotation.Log.BusinessType;
import com.yuehuanghun.framework.exception.ValidateException;
import com.yuehuanghun.framework.json.JSON;
import com.yuehuanghun.framework.service.location.IpLocationManager;
import com.yuehuanghun.framework.util.IpUtils;
import com.yuehuanghun.framework.util.Kv;
import com.yuehuanghun.framework.util.ServletUtils;
import com.yuehuanghun.framework.util.StringUtils;
import com.yuehuanghun.framework.util.security.RSAUtils;
import com.yuehuanghun.framework.web.BaseResource;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import eu.bitwalker.useragentutils.UserAgent;

/**
 * 登录验证
 * 
 * @author yadmin
 */
@RestController
public class SysLoginController extends BaseResource {
	@Autowired
	private ISysMenuService menuService;

	@Autowired
	private SysPermissionService permissionService;

	@Autowired
	private ISysUserService sysUserService;
	
	@Autowired
	private CacheManager cacheManager;
	
	@Autowired
	private HttpServletRequest request;
	
	@Autowired
	private Executor globalThreadPool;
	
	@Autowired
	private ISysLogininforService sysLogininforService;
	
	@Autowired
	private IpLocationManager ipLocationManager;

	/**
	 * 用户登录
	 * 
	 * @param loginReq
	 * @return
	 * @throws Exception 
	 */
	@PostMapping("/login")
	public Kv login(@Validated @RequestBody String body) throws Exception { //加密的登录信息，在无https的情况下，能够一定程度上保证密码安全性
		LoginReq loginReq = null;
		try {
			String decryptData = new String(RSAUtils.decryptByPrivateKey(body, SystemProperties.getPrivateKey()), "UTF-8");
			loginReq = JSON.parseObject(decryptData, LoginReq.class);
			
			if(System.currentTimeMillis() - loginReq.getTimestamp() > 60 * 1000) { //有效期一分钟
				throw new ValidateException("登录信息无效");
			}
			
			String captcha = cacheManager.getCache(CacheKeys.CAPTCHA_CODE).get(loginReq.getCaptchaUuid(), String.class);		
			if(captcha == null || !StringUtils.equals(captcha, loginReq.getCaptcha())) {
				throw new ValidateException("验证码不正确");
			}
			
			SysUser user = sysUserService.getUserByUserName(loginReq.getUserName());
			if(user == null) {
				throw new ValidateException("账号或密码不正确");
			}
			
			if(SecurityUtils.matchesPassword(loginReq.getPassword(), user.getPassword())) {
				StpUtil.login(user.getUserName()); //用户会话为用户名
			} else {
				throw new ValidateException("账号或密码不正确");
			}
		} catch (Exception e) {
			if(loginReq != null) {
				loginLog(loginReq.getUserName(), Constants.FAIL, e.getMessage(), null);
			}
			
			throw e;
		}
		
		loginLog(loginReq.getUserName(), Constants.SUCCESS, null, StpUtil.getTokenValue());
		return Kv.by("token", StpUtil.getTokenValue());
	}
	
	private void loginLog(String username, String status, String error, String token) {
		final UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        boolean isLogin = StpUtil.isLogin();
        SaSession session = token == null ? null : StpUtil.getTokenSessionByToken(token);
        globalThreadPool.execute(() -> {
        	String address = ipLocationManager.getLocation(ip);
        	String os = userAgent.getOperatingSystem().getName();
            // 获取客户端浏览器
            String browser = userAgent.getBrowser().getName();
            // 封装对象
            SysLogininfor logininfor = new SysLogininfor();
            logininfor.setUserName(username);
            logininfor.setIpaddr(ip);
            logininfor.setLoginLocation(address);
            logininfor.setBrowser(browser);
            logininfor.setOs(os);
            logininfor.setMsg(StringUtils.left(error, 255));
            logininfor.setStatus(status);
            
            sysLogininforService.add(logininfor);
            
            if(Constants.SUCCESS.contentEquals(status)) {
            	SysUser userUpdate = new SysUser();
                userUpdate.setLoginDate(new Date());
                userUpdate.setLoginIp(ip);
                
                sysUserService.updateByLambdaCriteria(userUpdate, p -> p.eq(SysUser::getUserName, username));
            }
            
            if(isLogin) {
                session.set("loginInfo", logininfor);
            }
        });
	}

	@PostMapping("/logout")
	@Log(title = "用户管理", businessType = BusinessType.OTHER)
	public void logout() {
		StpUtil.logout();
	}

	/**
	 * 获取用户信息
	 * 
	 * @return 用户信息
	 */
	@GetMapping("getInfo")
	@SaCheckLogin
	public Kv getInfo() {
		SysUser user = sysUserService.getUserByUserName(UserUtils.getCurrentUserName());
		// 角色集合
		Set<String> roles = permissionService.getRolePermission(user);
		// 权限集合
		Set<String> permissions = permissionService.getMenuPermission(user);
		return Kv.by("user", user).set("roles", roles).set("permissions", permissions);
	}

	/**
	 * 获取路由信息
	 * 
	 * @return 路由信息
	 */
	@GetMapping("getRouters")
	@SaCheckLogin
	public List<RouterVo> getRouters() {
		SysUser user = sysUserService.getUserByUserName(UserUtils.getCurrentUserName());
		// 用户信息
		List<SysMenu> menus = menuService.getMenuTreeByUserId(user.getUserId());
		return menuService.buildMenus(menus);
	}
}
